package com.kaibes.core.base.util;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.charset.Charset;
import java.util.Base64;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

public class ZipUtils {

    public static Charset CHAR_SET = Charset.forName("utf-8");

    public static void zipDir(File zipFile, String root, File dir) {
        ZipOutputStream zipOut = null;
        try {
            zipOut = new ZipOutputStream(new FileOutputStream(zipFile));
            zipDir(zipOut, root, dir);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } finally {
            if (zipOut != null) {
                try {
                    zipOut.flush();
                } catch (IOException e) {
                }
                try {
                    zipOut.close();
                } catch (IOException e) {
                }
            }
        }
    }

    public static void zipDir(ZipOutputStream zipOut, String root, File dir) {
        File[] files = dir.listFiles();
        for (int i = 0; i < files.length; ++i) {
            zipFile(zipOut, root, files[i]);
        }
    }

    public static void zipFile(ZipOutputStream zipOut, String root, File file) {
        if (file.isFile()) {
            InputStream input = null;
            try {
                input = new FileInputStream(file);
                zipOut.putNextEntry(new ZipEntry(root + file.getName()));
                int temp;
                while ((temp = input.read()) != -1) {
                    zipOut.write(temp);
                }
            } catch (IOException e) {
            } finally {
                if (input != null) {
                    try {
                        input.close();
                    } catch (IOException e) {
                    }
                }
            }
        } else {
            String myroot;
            if (!root.isEmpty()) {
                myroot = root + file.getName() + File.separator;
            } else {
                myroot = file.getName() + File.separator;
            }
            zipDir(zipOut, myroot, file);
        }
    }

    public static void unZipFile(String inFilePath, String outDirPath) {
        unZipFile(new File(inFilePath), outDirPath);
    }
    
    public static void unZipFile(File inFile, String outDirPath) {
        unZipFile(inFile, outDirPath, CHAR_SET);
    }
    public static void unZipFile(String inFilePath, String outDirPath, Charset charset) {
    	unZipFile(new File(inFilePath), outDirPath, charset);
    }
    public static void unZipFile(File inFile, String outDirPath, Charset charset) {
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(inFile);
            unZipFile(fis, outDirPath, charset);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                }
            }
        }
    }

    public static String getRootDir(String inFilePath) {
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(new File(inFilePath));
            return getRootDir(fis);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                }
            }
        }
        return null;
    }
    
    public static String getRootDir(InputStream is) {
        try {
            ZipInputStream zis = new ZipInputStream(is);
            ZipEntry entry = zis.getNextEntry();
            zis.close();
            is.close();
            if (entry != null && entry.isDirectory()) {
                return entry.getName();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static boolean hasRootDir(String inFilePath) {
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(new File(inFilePath));
            return hasRootDir(fis);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                }
            }
        }
        return false;
    }
    public static boolean hasRootDir(InputStream is) {
        try {
            ZipInputStream zis = new ZipInputStream(is);
            ZipEntry first = zis.getNextEntry();
            ZipEntry second = zis.getNextEntry();
            zis.close();
            is.close();
            if ((first != null && first.isDirectory()) && (second==null||second.getName().contains(first.getName()))) {
                return true;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }
    
    public static boolean hasFile(String inFilePath, String filename) {
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(new File(inFilePath));
            return hasFile(fis, filename);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                }
            }
        }
        return false;
    }
    
    public static boolean hasFile(InputStream is, String filename) {
        ZipInputStream zis = new ZipInputStream(is);// 输入源zip路径
        BufferedInputStream bis = new BufferedInputStream(zis);
        try {
            ZipEntry entry = null;
            while ((entry = zis.getNextEntry()) != null) {
                if (entry.getName().endsWith(filename)) {
                    return true;
                }
            }
            bis.close();
            zis.close();
            is.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }
    
    public static void unZipFile(InputStream is, String outDirPath) {
        unZipFile(is, outDirPath, CHAR_SET);
    }

    public static void unZipFile(InputStream is, String outDirPath, Charset charset) {
        ZipInputStream zis = new ZipInputStream(is, charset);// 输入源zip路径
        BufferedInputStream bis = new BufferedInputStream(zis);
        File file = null;
        ZipEntry entry;
        
        try {
            while ((entry = zis.getNextEntry()) != null) {
                if (!entry.isDirectory()) {
                    file = new File(outDirPath, entry.getName());
                    if (!file.getParentFile().exists()) {
                        (new File(file.getParent())).mkdirs();
                    }
                    FileOutputStream fos = new FileOutputStream(file);
                    BufferedOutputStream bos = new BufferedOutputStream(fos);
                    int b;
                    while ((b = bis.read()) != -1) {
                        bos.write(b);
                    }
                    bos.flush();
                    bos.close();
                    fos.flush();
                    fos.close();
                    bos = null;
                    fos = null;
                }
            }
            bis.close();
            zis.close();
            is.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void unZipFileChannel(String inFilePath, String outDirPath) {
        try {
            ZipInputStream zis = new ZipInputStream(new FileInputStream(inFilePath));// 输入源zip路径
            ReadableByteChannel readChannel = Channels.newChannel(zis);
            File file = null;
            ZipEntry entry;
            try {
                while ((entry = zis.getNextEntry()) != null && !entry.isDirectory()) {
                    file = new File(outDirPath, entry.getName());
                    if (!file.getParentFile().exists()) {
                        (new File(file.getParent())).mkdirs();
                    }
                    RandomAccessFile outFile = new RandomAccessFile(file, "rw");
                    int readSize = 1024;
                    long index;
                    do {
                        index = outFile.getChannel().transferFrom(readChannel, outFile.getChannel().size(), readSize);
                    } while (index == readSize);
                    outFile.close();
                }
            } catch (IOException e) {
            }
            try {
                readChannel.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                zis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }

    public static void zipFileNotDir(ZipOutputStream zipOut, String name, File file) {
        if (file.isFile()) {
            try {
                InputStream input = new FileInputStream(file);
                zipOut.putNextEntry(new ZipEntry(name));
                int temp;
                while ((temp = input.read()) != -1) {
                    zipOut.write(temp);
                }
                input.close();
            } catch (IOException e) {
            }
        }
    }

    public static String gzip(String data) {
        return new String(Base64.getEncoder().encode(gzip(data.getBytes(CHAR_SET))), CHAR_SET);
    }

    public static byte[] gzip(byte[] data) {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        GZIPOutputStream gzip = null;
        try {
            gzip = new GZIPOutputStream(out);
            gzip.write(data);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (gzip != null) {
                try {
                    gzip.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return out.toByteArray();
//        return Base64.getEncoder().encode(out.toByteArray());
    }

    public static String gunzip(String data) {
        return new String(gunzip(Base64.getDecoder().decode(data.getBytes(CHAR_SET))), CHAR_SET);
    }

    public static byte[] gunzip(byte[] data) {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ByteArrayInputStream in = null;
        try {
            in = new ByteArrayInputStream(data);
//            in = new ByteArrayInputStream(Base64.getDecoder().decode(data));
            GZIPInputStream ginzip = new GZIPInputStream(in);

            byte[] buffer = new byte[1024];
            int offset;
            while ((offset = ginzip.read(buffer)) != -1) {
                out.write(buffer, 0, offset);
            }
            ginzip.close();
            in.close();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                out.close();
            } catch (IOException e) {
            }
        }

        return out.toByteArray();
    }

    public static final String zip(String data) {
        return new String(zip(data.getBytes(CHAR_SET)), CHAR_SET);
    }

    public static final byte[] zip(byte[] data) {
        ByteArrayOutputStream out = null;
        ZipOutputStream zout = null;
        try {
            out = new ByteArrayOutputStream();
            zout = new ZipOutputStream(out);
            zout.putNextEntry(new ZipEntry("0"));
            zout.write(data);
            zout.closeEntry();
            data = Base64.getEncoder().encode(out.toByteArray());
            zout.close();
            out.close();
        } catch (IOException e) {
        }
        return data;
    }

    public static final String unzip(String data) {
        return new String(unzip(data.getBytes(CHAR_SET)), CHAR_SET);
    }

    public static final byte[] unzip(byte[] data) {
        try {
            byte[] compressed = Base64.getDecoder().decode(data);
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            ByteArrayInputStream in = new ByteArrayInputStream(compressed);
            ZipInputStream zin = new ZipInputStream(in);
            zin.getNextEntry();
            byte[] buffer = new byte[1024];
            int offset;
            while ((offset = zin.read(buffer)) != -1) {
                out.write(buffer, 0, offset);
            }
            data = out.toByteArray();
            zin.close();
            in.close();
            out.close();
        } catch (IOException e) {
        }
        return data;
    }
}
